#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <unistd.h>
#include <functional>
#include <stdlib.h>
#include <sys/wait.h>

///////////////////////////////子进程要完成的任务/////////////////////////
void SyncDisk()
{
    std::cout << getpid() << ": 刷新数据到磁盘任务" << std::endl;
    sleep(1);
}

void Download()
{
    std::cout << getpid() << ": 下载数据到系统中" << std::endl;
    sleep(1);
}

void PrintLog()
{
    std::cout << getpid() << ": 打印日志到本地" << std::endl;
    sleep(1);
}

void UpdateStatus()
{
    std::cout << getpid() << ": 更新一次用户的状态" << std::endl;
    sleep(1);
}

typedef void (*task_t)();

task_t tasks[4] = {SyncDisk, Download, PrintLog, UpdateStatus};

////////////////////////////////////////////////////////////////////////

const int gprocessnum = 5;
using cb_t = std::function<void(int)>; // 对各种任务的类型进行封装

enum
{
    OK = 0,
    PIPE_ERROR,
    FORK_ERROR
};

// 子程序的任务执行入口
void DoTask(int fd)
{
    while (true)
    {
        int itask = 0;
        ssize_t n = read(fd, &itask, sizeof(itask));
        if (n == sizeof(itask))
        {
            if (itask >= 0 && itask < 4)
            {
                tasks[itask]();
            }
        }
        else if (n == 0)
        {
            std::cout << getpid() << ": task quit ..." << std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

class Channel
{
public:
    Channel(int wfd, pid_t sub_pid)
        : _wfd(wfd),
          _sub_pid(sub_pid)
    {
        _sub_name = "sub-channel-" + std::to_string(_sub_pid);
    }
    ~Channel() {}

    void ClosePipe()
    {
        std::cout << "关闭wfd: " << _wfd << std::endl;
        close(_wfd);
    }
    void Wait()
    {
        pid_t rid = waitpid(_sub_pid, NULL, 0);
        (void)rid;
        std::cout << "回收子进程: " << _sub_pid << std::endl;
    }

    // 规定读写 四字节
    void Write(int itask)
    {
        ssize_t n = write(_wfd, &itask, sizeof(itask));
        (void)n;
    }

    void PrintInfo()
    {
        printf("wfd: %d, who: %d, channel name: %s\n", _wfd, _sub_pid, _sub_name.c_str());
    }

private:
    int _wfd;              // 父进程的写 文件描述符
    pid_t _sub_pid;        // 管道文件所连接的子进程
    std::string _sub_name; // 子channel的名字
};

class Process_pool
{
public:
    Process_pool()
    {
        srand((unsigned int)time(nullptr) ^ getpid());
    }
    ~Process_pool() {}

    void Init(cb_t cb)
    {
        CreateProcessPool(cb);
    }
    void Debug()
    {
        for (auto &c : channels)
        {
            c.PrintInfo();
        }
    }
    void Run()
    {
        std::cout << "------------------------------------------------" << std::endl;
        int cnt = 5;
        while (cnt--)
        // while (true)
        {
            // 1.选择一个任务 -- 随机选取
            int itask = SelectTask();
            std::cout << "itask: " << itask << std::endl;
            // 2.选择一个管道文件和子进程 -- 轮询方式
            int index = SelectChannel();
            std::cout << "index: " << index << std::endl;
            // 3.写发送任务给指定的channel
            SendTask2Salver(itask, index);
            sleep(1);
        }
    }
    void Quit()
    {
        // version 2
        for (auto &channel : channels)
        {
            channel.ClosePipe();
            channel.Wait();
        }

        // version 1
        // for(auto& channel:channels)
        // {
        //     channel.ClosePipe();
        // }
        // for(auto& channel:channels)
        // {
        //     channel.Wait();
        // }
    }

private:
    int SelectTask()
    {
        int itask = rand() % 4;
        return itask;
    }
    int SelectChannel()
    {
        static int index = 0;
        int idx = index;
        index++;
        index %= channels.size();
        return idx;
    }
    void SendTask2Salver(int itask, int index)
    {
        if (itask >= 4 || itask < 0)
            return;
        if (index < 0 || index >= channels.size())
            return;
        channels[index].Write(itask);
    }
    void CreateProcessPool(cb_t cb)
    {
        // 1.创建多个进程和管道文件
        for (int i = 0; i < gprocessnum; i++)
        {
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                std::cerr << "pipe error..." << std::endl;
                exit(PIPE_ERROR);
            }
            pid_t id = fork();
            if (id < 0)
            {
                std::cerr << "fork error..." << std::endl;
                exit(FORK_ERROR);
            }
            else if (id == 0)
            {
                // child read
                if(!channels.empty())
                {
                    for(auto& channel : channels)
                    {
                        channel.ClosePipe();
                    }
                }
                close(pipefd[1]);
                // 子进程完成任务
                cb(pipefd[0]);
                exit(OK);
            }
            else
            {
                // 父进程
                close(pipefd[0]);
                // Channel ch(pipefd[1],id);
                channels.emplace_back(pipefd[1], id);
            }
        }
    }

private:
    std::vector<Channel> channels;
};

int main()
{
    Process_pool pp;
    // 初始化创建子进程和管道文件
    pp.Init(DoTask);
    pp.Debug();

    // 父进程控制子进程
    pp.Run();

    // 父进程回收子进程
    pp.Quit();

    return 0;
}